﻿using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Drawing;
using System.Data;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using System.Threading;

namespace AMOOF {

    public delegate void DelegateClear();
    public delegate void DelegateAppendCTEComp(object [] obj);
    public delegate void DelegateAppendVaRComp(object [] obj);
    public delegate void DelegateLoadRankedPDFPanel(Panel pdf_label, int index);
    public delegate void DelegateLoadIntegrationResultsForPDF(string pdf_name);
    public delegate void DelegatePopulateMoments(string pdf_name);


    public partial class OptimizeResults : UserControl {

        private Thread m_PeriThread;

        private DelegateLoadPDFPanel m_delegateLoadRankedPDFPanel;
        private DelegateAppendCTEComp m_delegateAppendCTEComp;
        private DelegateAppendVaRComp m_delegateAppendVaRComp;
        private DelegatePopulateMoments m_delegatePopulateMoments;

        private DelegateClear m_delegateClear;
        private DelegateLoadIntegrationResultsForPDF m_delegateLoadIntegrationResultsForPDF;
        private bool loaded = false;
        private Dictionary<int, Panel> pdf_displays = new Dictionary<int, Panel>();

        public OptimizeResults() {

            m_delegateAppendCTEComp = new DelegateAppendCTEComp(AppendValueToCTEComp);
            m_delegateAppendVaRComp = new DelegateAppendVaRComp(AppendValueToVaRComp);
            m_delegateLoadIntegrationResultsForPDF = new DelegateLoadIntegrationResultsForPDF(LoadIntegrationResultsForPDF);
            m_delegateClear = new DelegateClear(ClearCTEVaR);
            m_delegatePopulateMoments = new DelegatePopulateMoments(this.PopulateMoments);

            InitializeComponent();



            btnIntegrate.ForegroundImage = Reflector.GetResource<Bitmap>("integral");
        }

        private void ClearCTEVaR() {
            if (this.InvokeRequired) {
                m_delegateClear = new DelegateClear(ClearCTEVaR);
                this.Invoke(m_delegateClear);
            }
            else {
                dgvCTEComp.Rows.Clear();
                dgvVaRComp.Rows.Clear();
            }
        }




        private void AppendValueToCTEComp(object[] obj) {
            if (this.InvokeRequired) {
                m_delegateAppendCTEComp = new DelegateAppendCTEComp(AppendValueToCTEComp);
                this.Invoke(m_delegateAppendCTEComp, new object[] { obj });
            }
            else {
                
                if (dgvCTEComp.RowCount == 0) {
                    dgvCTEComp.RowCount = 3;
                    dgvCTEComp.Rows[0].HeaderCell.Value = "Data";
                    dgvCTEComp.Rows[1].HeaderCell.Value = "PDF";
                    dgvCTEComp.Rows[2].HeaderCell.Value = "Diff";
                }
                dgvCTEComp.Rows[0].Cells[(int)obj[0]].Value = String.Format("{0:0.0000####}", (double)obj[1]);
                dgvCTEComp.Rows[1].Cells[(int)obj[0]].Value = String.Format("{0:0.0000####}", (double)obj[2]);
                dgvCTEComp.Rows[2].Cells[(int)obj[0]].Value = String.Format("{0:0.0000####}", (double)obj[3]);
                //dgvCTEComp.Invalidate();
                //dgvCTEComp.Update();
            }
        }

        private void AppendValueToVaRComp(object[] obj) {
            if (this.InvokeRequired) {
                m_delegateAppendVaRComp = new DelegateAppendVaRComp(AppendValueToVaRComp);
                this.Invoke(m_delegateAppendVaRComp, new object[] { obj });
            }
            else {

                if (dgvVaRComp.RowCount == 0) {
                    dgvVaRComp.RowCount = 3;
                    dgvVaRComp.Rows[0].HeaderCell.Value = "Data";
                    dgvVaRComp.Rows[1].HeaderCell.Value = "PDF";
                    dgvVaRComp.Rows[2].HeaderCell.Value = "Diff";
                }
                dgvVaRComp.Rows[0].Cells[(int)obj[0]].Value = String.Format("{0:0.0000####}", (double)obj[1]);
                dgvVaRComp.Rows[1].Cells[(int)obj[0]].Value = String.Format("{0:0.0000####}", (double)obj[2]);
                dgvVaRComp.Rows[2].Cells[(int)obj[0]].Value = String.Format("{0:0.0000####}", (double)obj[3]);
                //dgvVaRComp.Invalidate();
                //dgvVaRComp.Update();
            }
        }

        private void LoadIntegrationResultsForPDF(string pdf_name) {
            if (this.InvokeRequired) {
                m_delegateLoadIntegrationResultsForPDF = new DelegateLoadIntegrationResultsForPDF(LoadIntegrationResultsForPDF);
                this.Invoke(m_delegateLoadIntegrationResultsForPDF, new object[] { pdf_name });
            }
            else {
                string prefix = FormAMOOF.Instance.GetReport(pdf_name).IsMixed ? "Mixed " : "";
                lblPDFName.Text = prefix + FormAMOOF.Instance.GetReport(pdf_name).PDF.name;
                PopulateCTEandVaR(pdf_name);
                FormAMOOF.Instance.EnableTab(4);
            }
        }

        [STAThread]
        private void PopulateCTEandVaR(object obj) {
            string pdf_name = (string)obj;
            Statistics stats = FormAMOOF.Instance.GetStatistics();
            var report = FormAMOOF.Instance.GetReport(pdf_name);
            
            if (report.Integral == null) {
                try {
                    report.Integral = Integrator.integratePeri(new MultiplePDF(new List<PDF> { report.PDF }, new List<double> { 1 }));
                }
                catch (Exception ex) {
                    Program.log(ex.Message + "\r\n" + ex.StackTrace);
                    MessageBox.Show(ex.Message);
                }
            }
            PopulateCTEandVaR(pdf_name);
        }

        private void PopulateCTEandVaR(string pdf_name) {
           PopulateMoments(pdf_name);
            Statistics stats = FormAMOOF.Instance.GetStatistics();
            var report = FormAMOOF.Instance.GetReport(pdf_name);
            this.ClearCTEVaR();
            if (report.Integral != null) {
                int count = 0;
                int percentile = 1;
                foreach (var kvp in stats.CTEs) {
                    this.AppendValueToCTEComp(new object[] { count, kvp.Value, report.Integral.getPercentile(percentile).leftCte, Math.Abs(kvp.Value - report.Integral.getPercentile(percentile).leftCte) });
                    this.AppendValueToVaRComp(new object[] { count, stats.VaRs[percentile], report.Integral.getPercentile(percentile).var, Math.Abs(kvp.Value - report.Integral.getPercentile(percentile).var) });
                    count++;
                    if (percentile == 1) {
                        percentile = 5;
                    }
                    else if (percentile == 95) {
                        percentile = 99;
                    }
                    else {
                        percentile += 5;
                    }
                }
            }

        }

        private void PopulateMoments(string pdf_name) {
            if (this.InvokeRequired) {
                m_delegatePopulateMoments = new DelegatePopulateMoments(PopulateMoments);
                this.Invoke(m_delegatePopulateMoments, new object[] { pdf_name });
            }
            else {
                Statistics stats = FormAMOOF.Instance.GetStatistics();
                var report = FormAMOOF.Instance.GetReport(pdf_name);

                for (int i = 1; i < 5; i++) {
                    pnlMomentsContainer.Controls["lblMoment" + i].Text = stats.DataMoments[i].ToString();
                    pnlMomentsContainer.Controls["lblMoment_" + i].Text = stats.DataMoments[-i].ToString();
                    pnlMomentsContainer.Controls["lblModelMoment" + i].Text = report.PDF.Moments[i].Equals(-2146826252) ? "Doesn't exist" : report.PDF.Moments[i].ToString();
                    pnlMomentsContainer.Controls["lblModelMoment_" + i].Text = report.PDF.Moments[-i].Equals(-2146826252) ? "Doesn't exist" : report.PDF.Moments[-i].ToString();
                }
            }

        }

        private void GeneratePDFs() {
            var reports = FormAMOOF.Instance.GetReports();
            pnlPDFResults.Controls.Clear();
            int offset = 0;
            Panel header = new Panel();
            header.Name = "pnlHeader";
            Label pdfHeader = new Label();
            Label errHeader = new Label();

            pdfHeader.Name = "lblPDFHeader";
            errHeader.Name = "lblErrHeader";

            pdfHeader.Size = new Size(254, 18);
            pdfHeader.Location = new Point(28, 8);
            pdfHeader.Font = new Font("Arial", 12, FontStyle.Bold);
            pdfHeader.Text = "PDF Name";

            errHeader.Size = new Size(254, 18);
            errHeader.Location = new Point(282, 8);
            errHeader.Font = new Font("Arial", 12, FontStyle.Bold);
            errHeader.Text = "Average Error";

            header.Controls.Add(pdfHeader);
            header.Controls.Add(errHeader);
            header.Size = new Size(487, 34);
            header.Location = new Point(0, header.Height * offset);
            try {
                pdf_displays.Add(offset, header);
            }
            catch (Exception) {
                pdf_displays[offset] = header;
            }
            pnlPDFResults.Controls.Add(header);
            offset++;

            

            foreach (var kvp in reports) {
                Panel p = new Panel();
                OnOffButton btn = new OnOffButton();
                Label pdf = new Label();
                Label err = new Label();

                string name = kvp.Value.IsMixed ? "Mixed" + kvp.Value.PDF.name.Replace(" ", "") : kvp.Value.PDF.name.Replace(" ", "");

                p.Name = "pnl" + name;
                btn.Name = "btn" + name;
                pdf.Name = "lbl" + name;

                btn.BackgroundImage = (Bitmap)Properties.Resources.ResourceManager.GetObject("btn_round_blue_off");

                btn.OnImage = "btn_round_blue_on";
                btn.OffImage = "btn_round_blue_off";

                btn.Location = new Point(0, 3);
                btn.Size = new Size(28, 28);

                pdf.Size = new Size(254, 18);
                pdf.Location = new Point(28, 8);
                pdf.Font = new Font("Arial", 12, FontStyle.Regular);
                pdf.Text = kvp.Value.IsMixed ? "Mixed " + kvp.Value.PDF.name : kvp.Value.PDF.name;

                p.Tag = offset;

                err.Size = new Size(254, 18);
                err.Location = new Point(282, 8);
                err.Font = new Font("Arial", 12, FontStyle.Regular);
                err.Text = kvp.Value.PDF.AverageError.ToString();
                err.Name = p.Name + "AverageError";

                btn.MouseClick += new MouseEventHandler(PDF_Selected);
                pdf.MouseClick += new MouseEventHandler(PDF_Selected);
                p.MouseClick += new MouseEventHandler(PDF_Selected);

                p.Controls.Add(btn);
                p.Controls.Add(pdf);
                p.Controls.Add(err);
                p.Size = new Size(487, 34);
                p.Location = new Point(0, p.Height * offset);
                try {
                    pdf_displays.Add(offset, p);
                }
                catch (Exception) {
                    pdf_displays[offset] = p;
                }
                //pnlPDFResults.Controls.Add(p);
                offset++;
            }

            var keys = pdf_displays.Keys;
            var values = pdf_displays.Values;

            for (int i = 1; i < keys.Count; i++) {
                var i1 = pdf_displays[keys.ElementAt(i)];
                for (int j = i + 1; j < keys.Count; j++) {
                    var i2 = pdf_displays[keys.ElementAt(j)];
                                      
                    if (double.Parse(i2.Controls[i2.Name + "AverageError"].Text) < double.Parse(i1.Controls[i1.Name + "AverageError"].Text)) {
                        var temp = i1; 
                        i1 = i2;
                        i2 = temp;
                        i1.Location = new Point(0,i1.Height * keys.ElementAt(i));
                        i2.Location = new Point(0,i2.Height * keys.ElementAt(j));
                        pdf_displays[keys.ElementAt(i)] = i1;
                        pdf_displays[keys.ElementAt(j)] = i2;
                    }
                }
            }

            foreach (var kvp in pdf_displays) {
                pnlPDFResults.Controls.Add(kvp.Value);
            }


        }

        private void PDF_Selected(object sender, EventArgs e) {
            gbxPDFSolverResults.Visible = loaded;
            btnIntegrate.Enabled = true;

            foreach (var kvp in pdf_displays) {
                try {
                    if (((OnOffButton)pdf_displays[kvp.Key].Controls["btn" + pdf_displays[kvp.Key].Name.Substring(3)]).Selected) {
                        ((OnOffButton)pdf_displays[kvp.Key].Controls["btn" + pdf_displays[kvp.Key].Name.Substring(3)]).Selected = false;
                        ((Label)pdf_displays[kvp.Key].Controls["lbl" + pdf_displays[kvp.Key].Name.Substring(3)]).ForeColor = Color.Black;
                        
                    }
                }
                catch (Exception ex) {
                    Program.log(ex.Message + "\r\n" + ex.StackTrace);
                    //Oops!
                }
            }

            string reportKey = "";

            if (sender is OnOffButton) {
                ((OnOffButton)sender).Selected = true;
                reportKey = ((Control)sender).Parent.Name.Substring(3);
                ((Control)sender).Parent.Controls["lbl" + reportKey].ForeColor = Color.White;


            }
            else if (sender is Label) {
                int index = (int)((Control)sender).Parent.Tag;
                ((OnOffButton)pdf_displays[index].Controls["btn" + pdf_displays[index].Name.Substring(3)]).Selected = true;
                reportKey = ((Control)sender).Parent.Name.Substring(3);
                ((Control)sender).Parent.Controls["lbl" + reportKey].ForeColor = Color.White;
            }
            else if (sender is Panel) {
                ((OnOffButton)pdf_displays[(int)((Control)sender).Tag].Controls["btn" + pdf_displays[(int)((Control)sender).Tag].Name.Substring(3)]).Selected = true;
                reportKey = ((Control)sender).Name.Substring(3);
                ((Control)sender).Controls["lbl" + reportKey].ForeColor = Color.White;
            }

            this.Invalidate();
            this.Update();
            LoadIntegrationResultsForPDF(reportKey);

        }

        private void btnIntegrate_MouseClick(object sender, MouseEventArgs e) {
            m_PeriThread = new Thread(PopulateCTEandVaR);
            m_PeriThread.Start(lblPDFName.Text.Replace(" ", String.Empty));
        }

        private void OptimizeResults_Load(object sender, EventArgs e) {
            loaded = true;
            GeneratePDFs();
            if (lblPDFName.Text.Equals(String.Empty)) {
                btnIntegrate.Enabled = false;
            }
        }
    }
}
